//
//  UIKit+Swizzle.swift
//  ACFrameworkSwift
//
//  Created by c5342925 on 2022/4/1.
//
/**
 Swizzle methods to support various themes where in the extension of UIKit.
 */

import Foundation

// MARK: - UIView Extension

extension UIView {

    public static func swizzleMethodForThemingView() {
        [
            #selector(setter: backgroundColor) // like `setBackgroundColor`
        ].forEach { sel in
            // the swizzle method which addition `ac_` prefix.
            swizzleMethod(Self.classForCoder(), originalSelector: sel, swizzledSelector: Selector("ac_\(sel.description)"))
        }
    }
    
    @objc func ac_setBackgroundColor(_ color: UIColor?) {
        var newColor = color
        if let provider = newColor?.themeProvider {
            let key = handleMethodDescription()
            self.providers[key] = { theme in
                self.ac_setBackgroundColor(provider(theme))
            }
            newColor = provider(ACThemeManager.share.currentTheme)
        }
        ac_setBackgroundColor(newColor)
    }
}

// MARK: - UIButton Extension

extension UIButton {
    
    static func swizzleMethodForThemingButton() {
        [
            #selector(setTitleColor(_:for:))
        ].forEach { sel in
            swizzleMethod(Self.classForCoder(), originalSelector: sel, swizzledSelector: Selector("ac_\(sel.description)"))
        }
    }
    
    @objc func ac_setTitleColor(_ color: UIColor?, forState: UIControl.State) {
        var newColor = color
        if let provider = newColor?.themeProvider {
            let key = handleMethodDescription(suffix: "\(forState.rawValue)")
            self.providers[key] = { theme in
                self.ac_setTitleColor(provider(theme), forState: forState)
            }
            newColor = provider(ACThemeManager.share.currentTheme)
        }
        ac_setTitleColor(newColor, forState: forState)
    }
}


//MARK: - Swizzle

/// Exchange method.
func swizzleMethod(_ aClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
    if let originalMethod = class_getInstanceMethod(aClass, originalSelector),
        let swizzledMehod = class_getInstanceMethod(aClass, swizzledSelector) {
        let didAddMethod: Bool = class_addMethod(aClass, originalSelector, method_getImplementation(swizzledMehod), method_getTypeEncoding(swizzledMehod))
        if didAddMethod {
            class_replaceMethod(aClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        }else {
            method_exchangeImplementations(originalMethod, swizzledMehod)
        }
    }
}

/// Delete `(_`, `)` in method description. eg: `setBackgroundColor(_:)` to `setBackgroundColor:`
func handleMethodDescription(_ method: String = #function, suffix: String = "") -> String {
    var method = method
    ["(_", ")"].forEach { key in
        if method.contains(key) {
            method = method.replacingOccurrences(of: key, with: "")
        }
    }
    if !suffix.isEmpty {
        method = "\(method) + \(suffix)"
    }
    return method
}
